package pl.vanta.mjuzik;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.swing.event.ListSelectionEvent;

import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;
import pl.vanta.mjuzik.engine.player.PlayerStatus;
import pl.vanta.mjuzik.formats.FormatFactory;
import pl.vanta.mjuzik.formats.IFileInfo;
import pl.vanta.mjuzik.gui.SavePlaylistFileChooserOptionsPanel;
import pl.vanta.mjuzik.gui.listeners.IPlayerControllerPanel;
import pl.vanta.mjuzik.gui.listeners.PlayerControllerListener;
import pl.vanta.mjuzik.gui.listeners.PlayerLoadedListener;
import pl.vanta.mjuzik.gui.listeners.PlayerProgressListener;
import pl.vanta.mjuzik.gui.listeners.PlaylistButtonsListener;
import pl.vanta.mjuzik.gui.listeners.PlaylistListener;
import pl.vanta.mjuzik.model.AudioFile;
import pl.vanta.mjuzik.playlists.PlaylistFormatFactory;
import pl.vanta.mjuzik.services.ConfigurationService;
import pl.vanta.mjuzik.services.DialogManager;
import pl.vanta.mjuzik.utils.AudioFileComparator;
import pl.vanta.utils.gui.tables.IVTable;
import pl.vanta.utils.gui.utils.FileTransferListener;

import com.google.inject.Inject;

public class ApplicationController implements PlaylistButtonsListener, PlayerControllerListener, PlaylistListener,
		BasicPlayerListener, FileTransferListener
{
	private IVTable<AudioFile>					table;
	private IPlayerControllerPanel				controllerPanel;

	private BasicPlayer							player;

	private AudioFile							currentFile;
	private int									currentFileLength;

	private Collection<PlayerProgressListener>	progressListeners;
	private Collection<PlayerLoadedListener>	loadListeners;

	final DialogManager							dialogManager;
	final ConfigurationService					configurationService;

	@Inject
	public ApplicationController(DialogManager dialogManager, ConfigurationService configurationService)
	{
		this.player = new BasicPlayer();
		this.player.addBasicPlayerListener(this);

		this.progressListeners = new ArrayList<PlayerProgressListener>();
		this.loadListeners = new ArrayList<PlayerLoadedListener>();

		this.dialogManager = dialogManager;
		this.configurationService = configurationService;
	}

	public void init()
	{
		/*
		 * System.out.println(this.player.getMinimumGain());
		 * System.out.println(this.player.getMaximumGain());
		 * System.out.println(this.player.getMixerName());
		 * System.out.println(this.player.getPan());
		 * System.out.println(this.player.getPrecision());
		 * System.out.println(this.player.getLineCurrentBufferSize());
		 * System.out.println(this.player.getMixers());
		 * 
		 * for (String mixerName : this.player.getMixers()) {
		 * System.out.println("mixerName:" + mixerName);
		 * 
		 * Mixer mixer = this.player.getMixer(mixerName);
		 * 
		 * for (Control control : mixer.getControls()) {
		 * System.out.println("control type:" + control.getType()); } }
		 */
	}

	@Override
	public void addFiles()
	{
		File[] selected = this.dialogManager.getMusicFiles();

		if (selected != null)
		{
			addFilesToPlaylist(Arrays.asList(selected));
		}
	}

	@Override
	public void clearPlaylist()
	{
		this.table.removeAllItems();

		this.table.clearColoredRows();
	}

	@Override
	public void loadPlaylist()
	{
		File[] selected = this.dialogManager.getPlaylistFiles();

		if (selected != null && selected.length == 0)
		{
			List<File> files = MusicUtils.getAllFilesFromPlaylists(selected);

			this.table.removeAllItems();
			this.table.addItems(MusicUtils.convert(files,
					this.configurationService.getOptions().isCheckFilePresenceOnLoad(), this.configurationService.getOptions()
							.isLazyPlaylistLoading()));
		}
	}

	@Override
	public void removeFiles()
	{
		this.table.removeSelectedItems();
	}

	@Override
	public void savePlaylist()
	{
		SavePlaylistFileChooserOptionsPanel optionsPanel = new SavePlaylistFileChooserOptionsPanel();

		File saveFile = this.dialogManager.getPlaylistFile(optionsPanel);

		if (saveFile != null)
		{
			PlaylistFormatFactory.getInstance().savePlaylist(saveFile, this.table.getAllItems(),
					optionsPanel.isRelative());
		}
	}

	@Override
	public void next()
	{
		AudioFile temp = this.currentFile;

		if (temp == null)
		{
			temp = this.table.getSelectedItem();
		}

		int index = this.table.getItemIndex(temp);

		if (index != -1)
		{
			AudioFile af = this.table.getItem(index + 1);

			if (af == null)
			{
				// koniec playlisty najprawdopodobniej
				if (this.configurationService.getOptions().isCyclePlaylist())
				{
					// puszczamy pierwszy utwor z listy
					af = this.table.getItem(0);

					// bo playlista moze byc pusta
					if (af != null)
					{
						play(af);
					}
				}
			}
			else
			{
				play(af);
			}
		}
		else
		{
			// nie ma pliku na liscie juz, zagrajmy od poczatku

			this.dialogManager.showInfoDialog("Nie wybrano żadnej pozycji");
		}
	}

	@Override
	public void pause()
	{
		if (this.player.getStatus() == PlayerStatus.PLAYING)
		{
			this.player.pause();
		}
		else if (this.player.getStatus() == PlayerStatus.PAUSED)
		{
			this.player.resume();
		}
	}

	private void play(AudioFile audioFile)
	{
		if (audioFile == null)
		{
			this.dialogManager.showInfoDialog("Nie wybrano żadnej pozycji");
		}
		else
		{
			if (this.player.getStatus() == PlayerStatus.PLAYING || this.player.getStatus() == PlayerStatus.PAUSED)
			{
				this.player.stop();
			}

			if (audioFile.getFileInfo() == null)
			{
				FileInfo fi = FormatFactory.getInstance().getFileInfo(audioFile.getFile());

				if (fi == null)
				{
					this.dialogManager.showErrorDialog("Błąd podczas dekodowania pliku");
				}

				audioFile.setFileInfo(fi);
			}

			this.currentFile = audioFile;

			try
			{
				this.player.open(this.currentFile.getFile());
				this.player.play();

				this.player.setGain(this.controllerPanel.getVolume());
				this.player.setPan(this.controllerPanel.getBalance());

				this.table.colorItem(this.configurationService.getOptions().getPlayingFileSelectionColor(),
						this.currentFile);
			}
			catch (BasicPlayerException e)
			{
				this.dialogManager.showErrorDialog("Błąd podczas otwierania pliku");
			}
		}
	}

	@Override
	public void play()
	{
		AudioFile selection = this.table.getSelectedItem();

		if (selection == null)
		{
			selection = this.table.getItem(0);
		}

		play(selection);
	}

	@Override
	public void prev()
	{
		AudioFile temp = this.currentFile;

		if (temp == null)
		{
			temp = this.table.getSelectedItem();
		}

		int index = this.table.getItemIndex(temp);

		if (index != -1)
		{
			AudioFile af = this.table.getItem(index - 1);

			if (af == null)
			{
				// koniec playlisty najprawdopodobniej
				if (this.configurationService.getOptions().isCyclePlaylist())
				{
					// puszczamy ostatni utwor z listy
					af = this.table.getItem(this.table.getRowsCount() - 1);

					// bo playlista moze byc pusta
					if (af != null)
					{
						play(af);
					}
				}
			}
			else
			{
				play(af);
			}
		}
		else
		{
			this.dialogManager.showInfoDialog("Nie wybrano żadnej pozycji");
		}
	}

	@Override
	public void stop()
	{
		this.player.stop();

		this.currentFile = null;
	}

	@Override
	public void doubleClick(MouseEvent e)
	{
		play();
	}

	@Override
	public void popupClick(MouseEvent e)
	{
	}

	@Override
	public void keyPressed(KeyEvent e)
	{
		if (e.getKeyChar() == KeyEvent.VK_DELETE)
		{
			this.table.removeSelectedItems();
		}
		else if (e.getKeyChar() == KeyEvent.VK_ENTER)
		{
			e.consume();
			play();
		}
	}

	@Override
	public void selectionChanged(ListSelectionEvent e)
	{
	}

	public void setTable(IVTable<AudioFile> table)
	{
		this.table = table;
	}

	public void playerOpened()
	{
		firePlayerLoadedListeners(this.currentFile);

		this.currentFileLength = (Integer) this.currentFile.getFileInfo().getProperties().get("audio.length.bytes");
	}

	public void playerStoppped(int bytes)
	{
		System.out.println("STOP");

		if (bytes == -1)
		{
			bytes = this.currentFileLength;
		}

		float percent = calculatePercent(bytes);

		updateFileRating(percent);

		this.table.clearItemColor(this.currentFile);

		firePlayerUnloadListeners();
	}

	private void updateFileRating(float percent)
	{
		int value = 0;

		if (percent < 20)
		{
			value = -1;
		}
		else if (percent > 50)
		{
			value = +1;
		}

		this.currentFile.setRating(this.currentFile.getRating() + value);
	}

	@Override
	public void clearRating()
	{
		for (AudioFile audioFile : this.table.getSelectedItems())
		{
			audioFile.setRating(0);
		}

		this.table.refresh();
		this.table.clearSelection();
	}

	@Override
	public void opened(Object stream, Map<String, Object> properties)
	{
		// System.out.println(properties);
	}

	@Override
	public void progress(int bytesread, long microseconds, byte[] pcmdata, Map<String, Object> properties)
	{
		float progress = 1.0f * bytesread / this.currentFileLength;
		float percent = calculatePercent(bytesread);

		long milis = Math.round(progress * this.currentFile.getFileInfo().getPlayTime());

		firePlayerProgressListeners(milis, percent, bytesread);
	}

	@Override
	public void setController(BasicController controller)
	{

	}

	@Override
	public void stateUpdated(BasicPlayerEvent event)
	{
		switch (event.getEvent())
		{
			case STOPPED:
				playerStoppped(event.getPosition());
				break;

			case EOM:
				playerStoppped(this.currentFileLength);
				next();
				break;

			case OPENED:
				playerOpened();
				break;

			case PLAYING:
				playerPlaying();
				break;

			default:
				break;
		}
	}

	private void playerPlaying()
	{
		System.out.println("PLAY");
	}

	@Override
	public void setPosition(int bytes)
	{
		if (this.player.getStatus() == PlayerStatus.PLAYING || this.player.getStatus() == PlayerStatus.PAUSED)
		{
			try
			{
				this.player.seek(bytes);

				this.player.setGain(this.controllerPanel.getVolume());
				this.player.setPan(this.controllerPanel.getBalance());
			}
			catch (BasicPlayerException e)
			{
				e.printStackTrace();
			}
		}
	}

	public void addPlayerProgressListener(PlayerProgressListener listener)
	{
		this.progressListeners.add(listener);
	}

	public void removePlayerProgressListener(PlayerProgressListener listener)
	{
		this.progressListeners.remove(listener);
	}

	private void firePlayerProgressListeners(long time, float percent, int bytes)
	{
		for (PlayerProgressListener listener : this.progressListeners)
		{
			listener.updateProgress(time, percent, bytes);
		}
	}

	public void addPlayerLoadedListener(PlayerLoadedListener listener)
	{
		this.loadListeners.add(listener);
	}

	public void removePlayerLoadedListener(PlayerLoadedListener listener)
	{
		this.loadListeners.remove(listener);
	}

	private void firePlayerLoadedListeners(AudioFile audioFile)
	{
		for (PlayerLoadedListener listener : this.loadListeners)
		{
			listener.playerLoaded(audioFile);
		}
	}

	private void firePlayerUnloadListeners()
	{
		for (PlayerLoadedListener listener : this.loadListeners)
		{
			listener.playerUnloaded();
		}
	}

	@Override
	public void setVolume(double value)
	{
		try
		{
			this.player.setGain(value);
		}
		catch (BasicPlayerException e)
		{
			e.printStackTrace();
		}
	}

	@Override
	public void setBalance(double value)
	{
		try
		{
			this.player.setPan(value);
		}
		catch (BasicPlayerException e)
		{
			e.printStackTrace();
		}
	}

	private float calculatePercent(int bytes)
	{
		return bytes * 100f / this.currentFileLength;
	}

	public void setControllerPanel(IPlayerControllerPanel controllerPanel)
	{
		this.controllerPanel = controllerPanel;
	}

	private void addFilesToPlaylist(List<File> files)
	{
		if (files != null)
		{
			List<File> allFiles = MusicUtils.getAllFiles(files);

			List<AudioFile> audioFiles = MusicUtils.convert(allFiles, this.configurationService.getOptions()
					.isCheckFilePresenceOnLoad(), this.configurationService.getOptions().isLazyPlaylistLoading());

			this.table.addItems(audioFiles);

			if (this.configurationService.getOptions().isSortOnLoad())
			{
				this.table.sort(new AudioFileComparator());
			}
		}
	}

	@Override
	public void filesImported(List<File> files)
	{
		addFilesToPlaylist(files);
	}

	@Override
	public void importError()
	{
		this.dialogManager.showErrorDialog("Error during dragging files");
	}
}